import os, pickle

MONTHS = [
  "ZERO",
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
]

def nonrecursive(function):
  '''
  A decorator constructor for methods that forces them to return '...' if
  they're called recursively on a single object. That is, during the expansion
  of any call to the decorated method, calls to that method on the original
  object will return '...' instead of the function's normal return value.
  '''
  def modified(self, recursion_tracker=set()):
    if id(self) in recursion_tracker:
      return '...'
    recursion_tracker.add(id(self))
    result = function(self)
    recursion_tracker.remove(id(self))
    return result
  return modified

def load_facts():
  fin = open(os.path.join('data', 'known', 'facts.pkl'), 'r')
  facts, pages = pickle.load(fin)
  fin.close()
  return facts, pages

class Page(object):
  facts = None
  dates = None
  url = None
  empty = True
  def __init__(self, facts=None, dates=None, url=None):
    self.facts = facts or []
    self.dates = dates or []
    self.url = url
    self.empty = True
    if len(self.facts) > 1:
      self.empty = False
  def __repr__(self):
    return "Page(facts=" + repr(self.facts) + ", " +\
                 "dates=" + repr(self.dates) + ", " +\
                 "url=" + repr(self.url) + ")"
  def __str__(self):
    return str(self.url).replace('_', ' ')
  def add_fact(self, fact):
    if fact not in self.facts:
      self.facts.append(fact)
    if len(self.facts) > 1:
      self.empty = False
  def add_date(self, date):
    if date not in self.dates:
      self.dates.append(date)
  def remove_fact(self, fact):
    if fact in self.facts:
      self.facts.remove(fact)
    if len(self.facts) <= 1:
      self.empty = True
  def remove_date(self, date):
    if date in self.dates:
      self.dates.remove(date)

class Fact(object):
  """
  A common base class for all facts. Mostly just useful for isinstance() tests.
  """
  name = ""
  pronoun = "it"
  page = None
  def __init__(self, name="unnamed fact", pronoun="it", page=None):
    self.name = name
    self.page = page
    self.pronoun = pronoun
  @nonrecursive
  def __repr__(self):
    return "Fact(name=" + repr(self.name) + ", page=" + repr(self.page) + ")"
  def __str__(self):
    if self.name:
      return str(self.name)
    return "a nameless fact"


class Date(object):
  century = None
  decade = None
  year = None
  month = None
  day = None
  page = None

  def __init__(self, day=None, month=None, year=None, decade=None, 
      century=None, page=None):
    self.day = day
    self.month = month
    self.year = year
    self.decade = decade
    self.century = century
    if self.year != None:
      self.decade = (self.year / 10) * 10
    if self.decade != None:
      self.century = (self.decade / 100) * 100
    self.page = page
    #if self.year == None:
    #  if decade != None:
    #    year = decade
    #  elif century != None:
    #    year = century

  def __str__(self):
    if (self.day):
      return MONTHS[self.month] + " " + str(self.day) + ", " + str(self.year)
    if (self.month):
      return MONTHS[self.month] + " " + str(self.year)
    if(self.year) :
      return str(self.year)
    if (self.decade):
      return str(self.decade) + "s"
    else:
      return str(self.century) + "s"

  def string (self):
    s = str(self)
    if ' ' in s:
      return s
    if s[-1] == 's':
      return "the " + s
    return s

  def within(self, begin, end):
    selfyear = self.year or self.decade or self.century
    beginyear = begin.year or begin.decade or begin.century
    endyear = end.year or end.decade or end.century
    return beginyear <= selfyear <= endyear


class Location(Fact): 
   """
   A Location represents a physical place. Locations have a set of events that
   occured within them, as well as a set of locations that they are within and
   locations that they contain (these lists may be partial, and should be
   expounded via transitive closure).
   """
   events = None
   contains = None
   within = None

   def __init__(
     self,
     name="unnamed location",
     events=None,
     contains=None,
     within=None,
     pronoun="it",
     page=None,
   ):
      self.name = name
      self.events = events or set()
      self.contains = contains or set()
      self.within = within or set()
      self.pronoun = pronoun
      self.page = page
   @nonrecursive
   def __repr__(self):
     return "Location(name=" + repr(self.name) + ", " +\
                     "events=" + repr(self.events) + ", " +\
                     "contains=" + repr(self.contains) + ", " +\
                     "within=" + repr(self.within) + ", " +\
                     "pronoun=" + repr(self.pronoun) + ")"


class Person(Fact):
   """
   A Person represents a historical personage. People have Events that signify
   their birth and death, as well as a general set of events that they
   participated in and a complimentary set of events that they caused.
   """
   birth = None
   death = None
   caused = set()
   participated = set()
   def __init__(
     self,
     name="unnamed person",
     birth=None,
     death=None,
     caused=None,
     participated=None,
     pronoun="ey",
     page=None,
   ):
      self.name = name
      self.birth = birth
      self.death = death
      self.caused = caused or set()
      self.participated = participated or set()
      self.pronoun = pronoun
      self.page = page
   @nonrecursive
   def __repr__(self):
     return "Person(name=" + repr(self.name) + ", " +\
                   "birth=" + repr(self.birth) + ", " +\
                   "death=" + repr(self.death) + ", " +\
                   "caused=" + repr(self.caused) + ", " +\
                   "participated=" + repr(self.participated) + ", " +\
                   "pronoun=" + repr(self.pronoun) + ")"


class Event(Fact):
   """
   An event represents an abstract occurrence. It has start and end times, and
   a location in which it occurred. It also has a list of events that
   contributed to it causally, and to which it contributed causally.
   """
   start = None
   end = None
   location = None
   causes = None
   results = None
   instigators = None
   participants = None
   subject = None
   verb = None
   object = None
   def __init__(
     self,
     name="unnamed event",
     start=None,
     end=None,
     when=None,
     where=None,
     causes=None,
     results=None,
     instigators=None,
     participants=None,
     subject=None,
     verb=None,
     object=None,
     pronoun="it",
     page=None,
   ):
      self.name = name
      if start or end:
        self.start = start
        self.end = end
      elif when:
        self.start = when
        self.end = when
      self.location = where
      self.causes = causes or set()
      self.results = results or set()
      self.instigators = instigators or set()
      self.participants = participants or set()
      self.subject = subject
      self.verb = verb
      self.object = object
      self.pronoun = pronoun
      self.page = page
   @nonrecursive
   def __repr__(self):
     return "Event(name=" + repr(self.name) + ", " +\
                  "start=" + repr(self.start) + ", " +\
                  "end=" + repr(self.end) + ", " +\
                  "location=" + repr(self.location) + ", " +\
                  "causes=" + repr(self.causes) + ", " +\
                  "results=" + repr(self.results) + ", " +\
                  "instigators=" + repr(self.instigators) + ", " +\
                  "participants=" + repr(self.participants) + ", " +\
                  "subject=" + repr(self.subject) + ", " +\
                  "verb=" + repr(self.verb) + ", " +\
                  "object=" + repr(self.object) + ", " +\
                  "pronoun=" + repr(self.pronoun) + ")"


class Group(Fact):
   """
   A Group is any organizational unit of people. Groups have a list of members.
   """
   members = None
   def __init__(
     self,
     name="unnamed group",
     members=None,
     pronoun="they",
     page=None,
   ):
      self.name = name
      self.members = members or set()
      self.pronoun = pronoun
      self.page = page
   @nonrecursive
   def __repr__(self):
     return "Group(name=" + repr(self.name) + ", " +\
                  "members=" + repr(self.members) + ", " +\
                  "pronoun=" + repr(self.pronoun) + ")"


class Object(Fact):
   """
   An Object is an inanimate artifact (presumably of historical significance).
   Objects have a creator (which might be a Person or Group), an Event that
   signifies their creation, and a location.
   """
   creation = None
   creator = None
   location = None
   def __init__(
     self,
     name="unnamed object",
     creation=None,
     creator=None,
     location=None,
     pronoun="it",
     page=None,
   ):
      self.name = name
      self.creation = creation
      self.creator = creator
      self.location = location
      self.pronoun = pronoun
      self.page = page
   @nonrecursive
   def __repr__(self):
     return "Group(name=" + repr(self.name) + ", " +\
                  "creation=" + repr(self.creation) + ", " +\
                  "creator=" + repr(self.creator) + ", " +\
                  "location=" + repr(self.location) + ", " +\
                  "pronoun=" + repr(self.pronoun) + ")"
